package util

import (
	"bytes"
	"htgis/monitor/common/model"
	"log"
	"math/rand"
	"strings"
	"sync"
	"time"
)

var (
	// TransferClientsLock read write lock for TransferClients
	TransferClientsLock = new(sync.RWMutex)
	// TransferClients is a sealed jsonrpc client
	TransferClients = map[string]*SingleConnRPCClient{}
)

// SendToTransfer prepares params and send metrics to transfer module
func SendToTransfer(metricValues []*model.MetricValue) {
	if len(metricValues) == 0 {
		return
	}

	tags := Config().DefaultTags
	if len(tags) > 0 {
		var buf bytes.Buffer
		tagsList := []string{}
		for k, v := range tags {
			buf.Reset()
			buf.WriteString(k)
			buf.WriteString("=")
			buf.WriteString(v)
			tagsList = append(tagsList, buf.String())
		}
		defaultTag := strings.Join(tagsList, ",")

		for index, metricValue := range metricValues {
			buf.Reset()
			if metricValue.Tags == "" {
				metricValue.Tags = defaultTag
			} else {
				buf.WriteString(metricValues[index].Tags)
				buf.WriteString(",")
				buf.WriteString(defaultTag)
				metricValue.Tags = buf.String()
			}
		}
	}

	var resp model.TransferResponse
	sendMetrics(metricValues, &resp)
}

// sendMetrics sends metric values to transfer module
func sendMetrics(metrics []*model.MetricValue, resp *model.TransferResponse) {
	rand.Seed(time.Now().UnixNano())
	for _, i := range rand.Perm(len(Config().Transfer.Addrs)) {
		addr := Config().Transfer.Addrs[i]

		conn := getTransferClient(addr)
		if conn == nil {
			conn = initTransferClient(addr)
		}

		if updateMetrics(conn, metrics, resp) {
			break
		}
	}
}

func getTransferClient(addr string) *SingleConnRPCClient {
	TransferClientsLock.RLock()
	defer TransferClientsLock.RUnlock()

	if conn, ok := TransferClients[addr]; ok {
		return conn
	}

	return nil
}

func initTransferClient(addr string) *SingleConnRPCClient {
	var conn = &SingleConnRPCClient{
		RPCServer: addr,
		Timeout:   time.Duration(Config().Transfer.Timeout) * time.Millisecond,
	}
	TransferClientsLock.Lock()
	defer TransferClientsLock.Unlock()
	TransferClients[addr] = conn
	return conn
}

func updateMetrics(conn *SingleConnRPCClient, metrics []*model.MetricValue, resp *model.TransferResponse) bool {
	err := conn.Call("Transfer.Update", metrics, resp)
	if err != nil {
		log.Println("Call Transfer. Update fail: ", conn, err)
		return false
	}
	return true
}
